package com.gzs.learn.payment.service.impl;

import cn.felord.payment.alipay.AliPayClientContainer;
import cn.felord.payment.wechat.WechatPayProperties;
import cn.felord.payment.wechat.WechatPayProperties.V3;
import cn.felord.payment.wechat.v3.*;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.gzs.learn.common.util.BeanUtil;
import com.gzs.learn.common.util.JsonUtil;
import com.gzs.learn.common.util.SpringContextUtil;
import com.gzs.learn.inf.PageResponseDto;
import com.gzs.learn.inf.enums.StatusEnums;
import com.gzs.learn.payment.config.properties.AliPayCommonProperties;
import com.gzs.learn.payment.inf.dto.web.PaymentChannel;
import com.gzs.learn.payment.inf.dto.web.PaymentChannel.PubPrivPair;
import com.gzs.learn.payment.inf.dto.web.req.PaymentChannelSearchDto;
import com.gzs.learn.payment.mapper.PaymentChannelConfigMapper;
import com.gzs.learn.payment.model.po.PaymentChannelConfig;
import com.gzs.learn.payment.pay.PaymentTypeEnum;
import com.gzs.learn.payment.service.IPaymentChannelConfigService;
import com.gzs.learn.payment.util.EncryptUtil;
import com.gzs.learn.payment.util.PageUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Base64;
import java.util.Date;
import java.util.List;

import static cn.felord.payment.wechat.WechatPayConfiguration.CERT_ALIAS;

/**
 * 支付配置Service业务层处理
 *
 * @author guanzhisong
 * @date 2021-08-25
 */
@Service
@RequiredArgsConstructor
public class PaymentChannelConfigServiceImpl implements IPaymentChannelConfigService {
    private final AliPayCommonProperties aliPayCommonProperties;
    private final PaymentChannelConfigMapper channelConfigMapper;

    /**
     * 查询支付配置
     *
     * @param id 支付配置ID
     * @return 支付配置
     */
    @Override
    @Transactional(readOnly = true)
    public PaymentChannelConfig getConfigById(Long id) {
        return channelConfigMapper.selectById(id);
    }

    @Override
    @Transactional(readOnly = true)
    public PaymentChannel getChannelById(Long id) {
        return convert2Inf(channelConfigMapper.selectById(id));
    }

    @Override
    @Transactional(readOnly = true)
    public PaymentChannel getByChannelId(Integer channelId) {
        PaymentChannelConfig config = channelConfigMapper.getByChannelId(channelId);
        return convert2Inf(config);
    }


    /**
     * 查询支付配置列表
     *
     * @param searchDto 搜索参数
     * @return 支付配置
     */
    @Override
    @Transactional(readOnly = true)
    public PageResponseDto<PaymentChannel> searchList(PaymentChannelSearchDto searchDto) {
        Page<?> page = new Page<>(searchDto.getPageNum(), searchDto.getPageSize());
        IPage<PaymentChannelConfig> pageInfo = channelConfigMapper.searchList(page, searchDto);

        List<PaymentChannelConfig> channelConfigs = pageInfo.getRecords();
        List<PaymentChannel> channels = Lists.newArrayListWithExpectedSize(channelConfigs.size());
        for (PaymentChannelConfig channelConfig : channelConfigs) {
            PaymentChannel paymentChannel = convert2Inf(channelConfig);
            channels.add(paymentChannel);
        }
        return PageUtil.buildPageResponse(channels, (int) pageInfo.getTotal(), page);
    }

    /**
     * 新增支付配置
     *
     * @param paymentChannel 支付配置
     * @return 结果
     */
    @Override
    @Transactional
    public int insertPaymentChannelConfig(PaymentChannel paymentChannel) {
        if (getByChannelId(paymentChannel.getChannel().intValue()) != null) {
            // 已经存在指定支付渠道的配置
            return 0;
        }
        paymentChannel.setCreateTime(new Date());
        return channelConfigMapper.insert(convert2Po(paymentChannel));
    }

    /**
     * 修改支付配置
     *
     * @param paymentChannel 支付配置
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updatePaymentChannelConfig(PaymentChannel paymentChannel) {
        PaymentChannel channelConfig = getByChannelId(paymentChannel.getChannel().intValue());
        if (channelConfig != null && !channelConfig.getId().equals(paymentChannel.getId())) {
            // 已经存在指定支付渠道的配置
            return 0;
        }
        paymentChannel.setUpdateTime(new Date());
        return channelConfigMapper.updateById(convert2Po(paymentChannel));
    }

    private PaymentChannel convert2Inf(PaymentChannelConfig config) {
        if (config == null) {
            return null;
        }
        PaymentChannel paymentChannel = new PaymentChannel();
        BeanUtil.copyProperties(config, paymentChannel);
        String cipher = config.getPubPrivKey();
        if (StringUtils.isNotBlank(cipher)) {
            try {
                cipher = EncryptUtil.aesDecrypt(cipher);
                PubPrivPair ppp = JsonUtil.parseObject(cipher, PubPrivPair.class);
                if (ppp.getType() == 0) {
                    paymentChannel.setPubKey(ppp.getPub());
                    paymentChannel.setPrivKey(ppp.getPriv());
                } else {
                    paymentChannel.setPkcsContent(ppp.getCertContent());
                    paymentChannel.setPkcsPwd(ppp.getCertPwd());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return paymentChannel;
    }

    private PaymentChannelConfig convert2Po(PaymentChannel channel) {
        if (channel == null) {
            return null;
        }
        PaymentChannelConfig config = new PaymentChannelConfig();
        BeanUtil.copyProperties(channel, config);
        config.setId(channel.getId() == null || channel.getId() <= 0 ? null : channel.getId());
        if (StringUtils.isNotBlank(channel.getPkcsContent())) {
            try {
                String cipher = EncryptUtil.aesEncrypt(channel.getPkcsContent());
                config.setPubPrivKey(cipher);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return config;
    }

    @Override
    @Transactional
    public List<PaymentChannelConfig> selectAllEnableChannel() {
        Wrapper<PaymentChannelConfig> wrapper = Wrappers.lambdaQuery(new PaymentChannelConfig())
                .eq(PaymentChannelConfig::getStatus, StatusEnums.ENABLE.getCode());
        List<PaymentChannelConfig> configs = channelConfigMapper.selectList(wrapper);
        for (PaymentChannelConfig c : configs) {
            try {
                c.setPubPrivKey(EncryptUtil.aesDecrypt(c.getPubPrivKey()));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return configs;
    }

    @Override
    public void init() {
        List<PaymentChannelConfig> configs = selectAllEnableChannel();
        for (PaymentChannelConfig c : configs) {
            PaymentTypeEnum paymentTypeEnum = PaymentTypeEnum.convert(c.getPaymentType());
            if (paymentTypeEnum == null) {
                continue;
            }
            switch (paymentTypeEnum) {
                case ALIPAY:
                    initAlipayConfig(c);
                    break;
                case WECHAT:
                    initWechatConfig(c);
                    break;
                case UNIONPAY:
                    initUnionpayConfig(c);
                    break;
                default:
                    break;
            }
        }

        registerWechatBeanIfNecessary();
    }

    // 注册微信支付所需要的bean
    private void registerWechatBeanIfNecessary() {
        WechatMetaContainer container = SpringContextUtil.getBean(WechatMetaContainer.class);
        // 无微信支付配置
        if (container.getTenantIds().size() <= 0) {
            return;
        }
        SignatureProvider signatureProvider = null;
        if (SpringContextUtil.getBean(SignatureProvider.class) == null) {
            signatureProvider = new SignatureProvider(container);
            SpringContextUtil.registerBean("signatureProvider", signatureProvider);
        } else {
            signatureProvider = SpringContextUtil.getBean(SignatureProvider.class);
        }
        WechatPayClient wechatPayClient = null;
        if (SpringContextUtil.getBean(WechatPayClient.class) == null) {
            wechatPayClient = new WechatPayClient(signatureProvider);
            SpringContextUtil.registerBean("wechatPayClient", wechatPayClient);
        } else {
            wechatPayClient = SpringContextUtil.getBean(WechatPayClient.class);
        }
        WechatApiProvider wechatApiProvider = null;
        if (SpringContextUtil.getBean(WechatApiProvider.class) == null) {
            wechatApiProvider = new WechatApiProvider(wechatPayClient);
            SpringContextUtil.registerBean("wechatApiProvider", wechatApiProvider);
        } else {
            wechatApiProvider = SpringContextUtil.getBean(WechatApiProvider.class);
        }
    }

    private void initAlipayConfig(PaymentChannelConfig c) {
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        PaymentChannel.PubPrivPair pair = JsonUtil.parseObject(c.getPubPrivKey(), PubPrivPair.class);
        certAlipayRequest.setAlipayPublicCertContent(pair.getPub());
        certAlipayRequest.setPrivateKey(pair.getPriv());
        certAlipayRequest.setAppId(c.getAppId());
        certAlipayRequest.setFormat(aliPayCommonProperties.getFormat());
        certAlipayRequest.setServerUrl(aliPayCommonProperties.getServerUrl());
        certAlipayRequest.setCharset(aliPayCommonProperties.getCharset());
        certAlipayRequest.setSignType(aliPayCommonProperties.getSignType());

        String tenantId = String.valueOf(c.getChannel());
        AliPayClientContainer container = SpringContextUtil.getBean(AliPayClientContainer.class);
        try {
            AlipayClient client = new DefaultAlipayClient(certAlipayRequest);
            container.addAlipayClient(tenantId, client);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
    }

    private void initWechatConfig(PaymentChannelConfig c) {
        WechatMetaContainer container = SpringContextUtil.getBean(WechatMetaContainer.class);
        Long channel = c.getChannel();
        String tenantId = String.valueOf(channel);
        PaymentChannel.PubPrivPair pair = JsonUtil.parseObject(c.getPubPrivKey(), PubPrivPair.class);
        byte[] certs = Base64.getDecoder().decode(pair.getCertContent().getBytes());

        WechatPayProperties.V3 v3 = new V3();
        v3.setAppId(c.getAppId());
        v3.setAppSecret(c.getAppSecret());
        v3.setAppV3Secret(c.getAppSecret());
        v3.setCertContent(certs);
        v3.setMchId(c.getMchId());
        v3.setDomain(c.getSecDomain());

        KeyPairFactory pairFactory = new KeyPairFactory();
        WechatMetaBean wechatMetaBean = pairFactory.initWechatMetaBean(certs, CERT_ALIAS, pair.getCertPwd());

        wechatMetaBean.setV3(v3);
        wechatMetaBean.setTenantId(tenantId);

        container.addWechatMeta(tenantId, wechatMetaBean);
    }

    private void initUnionpayConfig(PaymentChannelConfig c) {

    }
}
